
<!-- saved from url=(0063)http://ghost-hack.com/post/jsexperiments/tunneler/tunneler.html -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
		<style type="text/css"> 
			body
			{
				font-family: Monospace;
				background-color: #222222;
				margin: 0px;
				overflow: hidden;
			}
		</style> 	
		<script type="text/javascript">
			var g;	
			var canvas;
			var width = window.innerWidth ;
			var height =  window.innerHeight ;
			var windowHalfX = window.innerWidth / 2;
			var windowHalfY = window.innerHeight / 2;
			var mouseX = 0;
			var mouseY = 0;	
			var pmouseX = 0;
			var pmouseY = 0;
			var mousePressed = false;
			var drawRate = 6;
			var frameCount = 0;
			
			var PI = Math.PI;
			var TWO_PI = 2*Math.PI;
			var HALF_PI = Math.PI*0.5;
			
			function init(){
				canvas = document.getElementById('canvas');			
				canvas.width = width;				
				canvas.height = height;	
								
				g = canvas.getContext('2d');		
				g.setTransform(1, 0, 0, 1, 0,0);				
			    document.addEventListener('mousemove', onDocumentMouseMove, false);								
			    document.addEventListener('mousedown', onDocumentMouseDown, false);
			    document.addEventListener('mouseup', onDocumentMouseUp, false);			    
   			    document.addEventListener('keydown', onDocumentKeyDown, false);			    
			    setup();
				drawInternal();			
			}
			
			function windowResize(){
//				width = window.innerWidth ;
//				height = window.innerHeight ;				
//				canvas.width = width;
//				canvas.height = height;
				g.setTransform(1, 0, 0, 1, 0,0);				
			}
			
			function onDocumentMouseMove(event) {
			    mouseX = event.clientX - canvas.offsetLeft;
			    mouseY = event.clientY - canvas.offsetTop;					    
			}		
			
			function onDocumentMouseDown(event) {
			    mousePressed = true;		
			    mousePressedEvent();
			}				
			
			function onDocumentMouseUp(event) {
			    mousePressed = false;			
			}		
			
			function onDocumentKeyDown(event){
				//setup();
			}
			
			function drawInternal(){				
				draw();
				setTimeout('drawInternal()', drawRate);			
				pmouseX = mouseX;
				pmouseY = mouseY;		
				frameCount++;
			}
			
			function clear(){
				g.clearRect(0,0,width,height);			
			}
			
			function line(x1, y1, x2, y2){
				g.beginPath();
				g.moveTo(x1,y1);
				g.lineTo(x2,y2);
				g.closePath();
				g.stroke();
			}
			
			function fillStrokeCircle(x,y,radius){
				if(radius<=0)
					radius = 0;
				g.beginPath();					
				g.arc(x, y, radius, 0, TWO_PI, true);
				g.closePath();
				g.fill();	
				g.stroke();
			}
			
			function fillCircle(x, y, radius){
				if(radius<=0)
					radius = 0;
				g.beginPath();					
				g.arc(x, y, radius, 0, TWO_PI, true);
				g.closePath();				
				g.fill();				
			}
			
			function strokeCircle(x, y, radius){
				if(radius<=0)
					radius = 0;
				g.beginPath();					
				g.arc(x,y, radius, 0, TWO_PI, true);
				g.stroke();							
			}
			
			function dist(ax, ay, bx, by) {
				var dx = bx - ax;
				dy = by - ay;
				return Math.sqrt(dx * dx + dy * dy + dz * dz);
			}		
			
			
			var currentRandom = Math.random;
			
			// Pseudo-random generator
			function Marsaglia(i1, i2) {
			  // from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c
			  var z=i1 || 362436069, w= i2 || 521288629;
			  var nextInt = function() {
				z=(36969*(z&65535)+(z>>>16)) & 0xFFFFFFFF;
				w=(18000*(w&65535)+(w>>>16)) & 0xFFFFFFFF;
				return (((z&0xFFFF)<<16) | (w&0xFFFF)) & 0xFFFFFFFF;
			  };
			 
			  this.nextDouble = function() {
				var i = nextInt() / 4294967296;
				return i < 0 ? 1 + i : i;
			  };
			  this.nextInt = nextInt;
			}
			Marsaglia.createRandomized = function() {
			  var now = new Date();
			  return new Marsaglia((now / 60000) & 0xFFFFFFFF, now & 0xFFFFFFFF);
			};		
		
			// Noise functions and helpers
			function PerlinNoise(seed) {
			  var rnd = seed !== undefined ? new Marsaglia(seed) : Marsaglia.createRandomized();
			  var i, j;
			  // http://www.noisemachine.com/talk1/17b.html
			  // http://mrl.nyu.edu/~perlin/noise/
			  // generate permutation
			  var p = new Array(512); 
			  for(i=0;i<256;++i) { p[i] = i; }
			  for(i=0;i<256;++i) { var t = p[j = rnd.nextInt() & 0xFF]; p[j] = p[i]; p[i] = t; }
			  // copy to avoid taking mod in p[0];
			  for(i=0;i<256;++i) { p[i + 256] = p[i]; } 
			  
			  function grad3d(i,x,y,z) {        
				var h = i & 15; // convert into 12 gradient directions
				var u = h<8 ? x : y,                 
					v = h<4 ? y : h===12||h===14 ? x : z;
				return ((h&1) === 0 ? u : -u) + ((h&2) === 0 ? v : -v);
			  }
		
			  function grad2d(i,x,y) { 
				var v = (i & 1) === 0 ? x : y;
				return (i&2) === 0 ? -v : v;
			  }
			  
			  function grad1d(i,x) { 
				return (i&1) === 0 ? -x : x;
			  }
			  
			  function lerp(t,a,b) { return a + t * (b - a); }
				
			  this.noise3d = function(x, y, z) {
				var X = Math.floor(x)&255, Y = Math.floor(y)&255, Z = Math.floor(z)&255;
				x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z);
				var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z;
				var p0 = p[X]+Y, p00 = p[p0] + Z, p01 = p[p0 + 1] + Z, p1  = p[X + 1] + Y, p10 = p[p1] + Z, p11 = p[p1 + 1] + Z;
				return lerp(fz, 
				  lerp(fy, lerp(fx, grad3d(p[p00], x, y, z), grad3d(p[p10], x-1, y, z)),
						   lerp(fx, grad3d(p[p01], x, y-1, z), grad3d(p[p11], x-1, y-1,z))),
				  lerp(fy, lerp(fx, grad3d(p[p00 + 1], x, y, z-1), grad3d(p[p10 + 1], x-1, y, z-1)),
						   lerp(fx, grad3d(p[p01 + 1], x, y-1, z-1), grad3d(p[p11 + 1], x-1, y-1,z-1))));
			  };
			  
			  this.noise2d = function(x, y) {
				var X = Math.floor(x)&255, Y = Math.floor(y)&255;
				x -= Math.floor(x); y -= Math.floor(y);
				var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y;
				var p0 = p[X]+Y, p1  = p[X + 1] + Y;
				return lerp(fy, 
				  lerp(fx, grad2d(p[p0], x, y), grad2d(p[p1], x-1, y)),
				  lerp(fx, grad2d(p[p0 + 1], x, y-1), grad2d(p[p1 + 1], x-1, y-1)));
			  };
		
			  this.noise1d = function(x) {
				var X = Math.floor(x)&255;
				x -= Math.floor(x);
				var fx = (3-2*x)*x*x;
				return lerp(fx, grad1d(p[X], x), grad1d(p[X+1], x-1));
			  };
			}
			
			//	these are lifted from Processing.js
			// processing defaults
			var noiseProfile = { generator: undefined, octaves: 4, fallout: 0.5, seed: undefined};
		
			function noise(x, y, z) {
			  if(noiseProfile.generator === undefined) {
				// caching
				noiseProfile.generator = new PerlinNoise(noiseProfile.seed);
			  }
			  var generator = noiseProfile.generator;
			  var effect = 1, k = 1, sum = 0;
			  for(var i=0; i<noiseProfile.octaves; ++i) {
				effect *= noiseProfile.fallout;        
				switch (arguments.length) {
				case 1:
				  sum += effect * (1 + generator.noise1d(k*x))/2; break;
				case 2:
				  sum += effect * (1 + generator.noise2d(k*x, k*y))/2; break;
				case 3:
				  sum += effect * (1 + generator.noise3d(k*x, k*y, k*z))/2; break;
				}
				k *= 2;
			  }
			  return sum;
			};					
			
			
			function Worm(x,y){
				this.x = x;
				this.y = y;
				this.lx = x;
				this.ly = y;
				this.heading = Math.sin(frameCount*0.083) * PI;;
				this.rotation = Math.random() * (PI / (Math.random() * 70));
				this.rate = 0;
				this.maxLength = 15 + (noise(frameCount * 0.0025, frameCount*0.1) * 15);
				this.detail = 2;
				this.thickness = 3;
				this.thicknessTarget = 5 + Math.random() * 10;
				
				var cIndex = parseInt( Math.random() * colors.length );
				this.c = colors[cIndex];
				this.life = 30 + Math.random() * 120;
				this.segments = new Array();
				this.ooo = true;
				this.counter = noise(frameCount * 0.1, frameCount*.1);			
				
				this.update = function(){
					this.life--;
					if(this.life > 0){
						this.thickness += (this.thicknessTarget-this.thickness) * 0.1;
						this.thickness += 0.1;
						if(this.thickness > this.thicknessTarget)
							this.thickness = this.thicknessTarget;
						this.heading += this.rotation;
						this.rate = Math.cos( this.counter / 200.0) * (10 + noise(frameCount*0.05) * 10);
						this.rotation = Math.sin(this.counter/this.rate) * (this.segments.length+1) * 0.010;
						this.counter++;
						
						var speedMod = (this.segments.length * this.segments.length * this.segments.length) * 0.0015 * this.thickness / this.thicknessTarget;
						var totalSpeed = (this.detail + speedMod);
						var nx = Math.cos(this.heading) * totalSpeed;
						var ny = Math.sin(this.heading) * totalSpeed;
						this.walk(nx,ny);
					}
					else{
						if(this.segments.length > 1){
							this.segments.pop();
						}
						else{
							this.thickness*=0.95;
							this.thickness-=0.2;
							if(this.thickness<0.1)
								this.ooo = true;
							return;
						}
					}
					
					this.ooo = true;
					for(var i=0; i<this.segments.length; i++){
						var segment = this.segments[i];
						if(segment.ooo() == false){
							this.ooo = false;
							break;
						}
					}									
				}
				
				this.walk = function(nx,ny){
					this.lx = this.x;
					this.ly = this.y;
					this.x += nx;
					this.y += ny;
					var newSegment = new Segment(this.lx,this.ly,this.x,this.y,this.thickness);
					this.segments.unshift(newSegment);
					
					if(this.segments.length > 1)
						this.segments[this.segments.length-1].smoothAgainst(this.segments[this.segments.length-2]);
					
					if(this.segments.length >= this.maxLength){
						this.segments.pop();
					}
					
				}
			}
			
			function Segment(x1,y1,x2,y2,thickness){
				this.x1 = x1;
				this.y1 = y1;
				this.x2 = x2;
				this.y2 = y2;
				this.thickness = thickness;
				
				var angle = Math.atan2(y2-y1,x2-x1);
				
				this.lAngle = angle - HALF_PI;
				var lDeltaX = Math.cos(this.lAngle) * thickness;
				var lDeltaY = Math.sin(this.lAngle) * thickness;
				this.leftX1 = x1 + lDeltaX;
				this.leftY1 = y1 + lDeltaY;
				this.leftX2 = x2 + lDeltaX;
				this.leftY2 = y2 + lDeltaY;
				
				this.rAngle = angle + HALF_PI;
				var rDeltaX = Math.cos(this.rAngle) * thickness;
				var rDeltaY = Math.sin(this.rAngle) * thickness;
				this.rightX1 = x1 + rDeltaX;
				this.rightY1 = y1 + rDeltaY;
				this.rightX2 = x2 + rDeltaX;
				this.rightY2 = y2 + rDeltaY;
				
				this.smoothAgainst = function(last){
					this.leftX1 = last.leftX2 = (last.leftX2 + this.leftX1) * 0.5;
					this.leftY1 = last.leftY2 = (last.leftY2 + this.leftY1) * 0.5;
					this.rightX1 = last.rightX2 = (last.rightX2 + this.rightX1) * 0.5;
					this.rightY1 = last.rightY2 = (last.rightY2 + this.rightY1) * 0.5;
				}
				
				this.ooo = function(){
					if(this.x1 < 0 || this.y1 < 0 || this.x1 > width || this.y1>height ||
					   this.x2 < 0 || this.y2 < 0 || this.x2 > width || this.y2>height)
					  return true;
					else
					  return false;				
				}
			}
			
			var worms;
			var colors = [];
			colors[0] = '#2cd9fe';
			colors[1] = '#2cfecf';
			colors[2] = '#373fdf';
			colors[3] = '#88fe1f';
			colors[4] = '#48d6ff';
			colors[5] = '#b3fcff';
			colors[6] = '#f76cad';
			colors[7] = '#505083';
			colors[8] = '#113a7e';
			colors[9] = '#014050';
			colors[10] = '#ccf3ef';
			colors[11] = '#009437';
			colors[12] = '#8fb300';
			
			
			function setup(){
				worms = new Array();
			}

			function draw(){
				clear();
				
				
				for(var w=0; w<worms.length; w++){
					var worm = worms[w];
					worm.update();
					g.fillStyle = worm.c;
					
					g.lineWidth = 2;
					g.strokeStyle = '#ffffff';					
					if(worm.segments.length>1){					
						g.beginPath();
						g.moveTo(worm.segments[0].leftX1, worm.segments[0].leftY1);						
						for(var i=0; i<worm.segments.length; i++){
							var segment = worm.segments[i];
							g.lineTo(segment.leftX1,segment.leftY1);
						}
						g.lineTo(worm.segments[worm.segments.length-1].rightX1, worm.segments[worm.segments.length-1].rightY1);
						for(var i=worm.segments.length-1; i>=0; i--){
							var segment = worm.segments[i];
							g.lineTo(segment.rightX1,segment.rightY1);
						}
						g.closePath();
//						g.stroke();
						g.fill();
						g.stroke();
					}		
					
					g.strokeStyle = '#ffffff';
					if(worm.segments.length>=1){
						var x = worm.segments[0].x1;
						var y = worm.segments[0].y1;	
						var thickness = worm.segments[0].thickness;
						if(worm.thickness<thickness)
							thickness = worm.thickness;
						if(thickness<=0)
							thickness = 0.000001;							
						g.beginPath();
						g.arc(x, y, thickness, worm.segments[0].lAngle, worm.segments[0].rAngle, false);
						g.closePath();
						//g.fill();
						g.stroke();
						
						var xEnd = worm.segments[worm.segments.length-1].x1;
						var yEnd = worm.segments[worm.segments.length-1].y1;
						thickness = worm.segments[worm.segments.length-1].thickness;
						if(worm.thickness<thickness)
							thickness = worm.thickness;						
						if(thickness<=0)
							thickness = 0.000001;						
						g.beginPath();
						g.arc(xEnd, yEnd, thickness, worm.segments[worm.segments.length-1].lAngle, worm.segments[worm.segments.length-1].rAngle, true);
						g.closePath();
						//g.fill();
						g.stroke();		
																
						thickness = worm.thickness-0.6;	
						if(worm.thickness<thickness)
							thickness = worm.thickness;							
						fillCircle(worm.segments[0].x1,worm.segments[0].y1,thickness);    
						thickness = worm.segments[worm.segments.length-1].thickness-0.6;							
						if(worm.thickness<thickness)
							thickness = worm.thickness;							
						fillCircle(worm.segments[worm.segments.length-1].x1,worm.segments[worm.segments.length-1].y1,thickness);    							
						
						if(worm.life>0){
							g.fillStyle = "#FFFFFF";
							fillCircle(worm.segments[0].x1,worm.segments[0].y1,thickness * 0.72);    
						}
					}					
				}		
				
				for(var w=0; w<worms.length; w++){
					var worm = worms[w];
					if(worm.ooo){
						worms.splice(w,1);
						w--;
					}				
				}
				
				if(frameCount %2 ==0 && mousePressed && worms.length < 50){
					var direction = Math.atan2(pmouseY-mouseY, pmouseX-mouseX) + PI;
					var newWorm = new Worm(mouseX,mouseY);
					worms.push( newWorm );
					if(mouseX!=pmouseX && mouseY!=pmouseY)
						newWorm.heading = direction;
				}
				
			}
			
			function mousePressedEvent(){
			}
			
			
		</script>	
		<link rel="stylesheet" type="text/css" href="./css/html5.css">
		<style>
		  body {
			font-family: 'Lobster', serif;
			font-size: 72px;
			color: #FFFFFF;
		  }
		</style>		
	</head>
	<body onload="init()" onresize="windowResize()">	
		<div style="position:relative;top:10px;z-index:1;-moz-user-select: none;-khtml-user-select: none;
  					user-select: none;">
			<center>
					Click Me ! Show you animation !
			</center>
		</div>
		<div style="z-index:4; position:absolute; top:0; left:0">		
			<canvas id="canvas" width="1280" height="629"></canvas>
		</div>
	

</body></html>
